/*!  \file  GKManager.cpp
 *	  \brief  

 *   \details   details
 *   \author    Yordan Madzhunkov German Research School for Simulation Sciences GmbH
 *   \version   1.0
 *   \date      10/31/2013 06:56:52 PM
 *   \copyright GNU Public License.
 */

#include "macros.h"
#include <iostream>
#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <float.h>
#include <iomanip>
#include <stdio.h>
#include "GK.h"
#include "GKManager.h"
#include "Lock.h"
using namespace std;
GKManager::GKManager(const int nGK_, const GK * nat_ ) {
	nGK   = nGK_;
	nat   = nat_;
	n     = nat->getN();
	allocate();
//	char buf[128];
//	sprintf(buf, "GKManager constructor 1, nGK=%d GKmat=%p",nGK, nat);
//	cout << buf << endl;
}
GKManager::GKManager(const int nGK_, const int n_) {
	nGK   = nGK_;
	nat   = NULL;
	n     = n_  ;
	allocate();

//	char buf[128];
//	sprintf(buf, "GKManager constructor 2, nGK=%d n=%d\n",nGK, n);
//	cout << buf ;

}
void GKManager::allocate() {
	locks      = new Lock   [nLocks     ];
	repStorage = new double [4 * n * nGK];
	owner      = new TaskID [nGK        ];
	reps       = new GK     [nGK        ];
	for (int i = 0; i < nGK; i++) {
		owner[i] = -1;
	}
}

GKManager::~GKManager() {
	delArr(locks);      
	delArr(repStorage); 
	delArr(owner);
	delArr(reps);
};
void GKManager::setUp (GK & gk, int gkID       ) const {
	gk.copyFields(*nat);
	gk.space          = 4 * gk.n;
	gk.data           = repStorage + gkID * gk.space;
	gk.representation = NGNPack;	
	gk.setPointers();
	gk.repIndex = 0;
	gk.minGammaIndex = 0;
}; 

GK * GKManager::obtainGK (const TaskID taskID) {


	int gkID = getGKID(taskID);
	int limit = 4 * nGK;
	int iter = 0;
	while (iter < limit) {
		while (owner[gkID] != -1 && iter++ < limit)
			gkID = (gkID + 1) % nGK;
		int lockID = getLockID(gkID);
		
		if ( owner[gkID] == -1 ) {
			if (locks[lockID].tryLock() ) {
				if ( owner[gkID] == -1 ) {
					owner[gkID] = taskID;
				} 
				locks[lockID].unlock();
			}
			if (owner[gkID] == taskID) {
				setUp(reps[gkID], gkID);	
//				char buf[128];
//				sprintf(buf, "obtainGK taskID=%d, gkID=%d, lockID=%d, %p\n",
//					taskID, gkID, lockID, &reps[gkID]);
//				cout << buf;
				return &reps[gkID];
			}
			
		}
	}
	MISSING;
	return  NULL;
}
const GK * GKManager::getGK    (const TaskID taskID) const {
	int gkID = getGKID(taskID);
	while (owner[gkID] != taskID )
		gkID = (gkID + 1) % nGK;
	return &reps[gkID];
}

// slighty slower version of getGK
// but more safe
const GK * GKManager::getGK_safe    (const TaskID taskID) const {
	int gkID = getGKID(taskID);
	int iter = 0;
	while (owner[gkID] != taskID && iter++ < nGK)
		gkID = (gkID + 1) % nGK;

	const GK * res = iter < nGK ? &reps[gkID] : NULL;
//	char buf[128];
//	sprintf(buf, "getGK_safe taskID=%d, gkID=%d, %p\n",
//	taskID, gkID, res);
//	cout << buf;
	return res;
 }

// releases the GK matrix
// assumes that there will be NO calls to getGK
// during the execution of release 
void GKManager::releaseGK(const TaskID taskID) {
	int gkID = getGKID(taskID);
	while (owner[gkID] != taskID )
		gkID = (gkID + 1) % nGK;
	owner[gkID] = -1;
//	char buf[128];
//	sprintf(buf, "release taskID=%d, gkID=%d\n",
//	taskID, gkID);
//	cout << buf;

}
